﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using DynORM.Interfaces;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.Schema;

namespace DynORM.ValueObjects
{
    [XmlRoot(ElementName = FieldDescriptor.FieldDescriptorTag)]
    public class FieldDescriptor : IFieldDescriptor, IXmlSerializable
    {
        private Type fieldType; //had to relax "readonly" restriction of this member in favor to init logic of xml serializer
        private string fieldName; //had to relax "readonly" restriction of this member in favor to init logic of xml serializer
        private List<object> attributes = new List<object>();

        public FieldDescriptor(Type fieldType, string fieldName)
        {
            this.fieldType = fieldType;
            this.fieldName = fieldName;
        }

        public FieldDescriptor(Type fieldType, string fieldName, object[] attributes)
        {
            this.fieldType = fieldType;
            this.fieldName = fieldName;
            this.attributes.AddRange(attributes);
        }

        //for serializer
        private FieldDescriptor()
        { }

        public const string FieldDescriptorTag = "FieldDescriptor";
        public const string FieldNameAttr = "FieldName";
        public const string FieldTypeAttr = "FieldType";
        public const string FieldAttributesAttr = "Attribute";

        public Type FieldType
        {
            get { return this.fieldType; }
        }

        public string FieldName
        {
            get { return this.fieldName; }
        }

        public object[] Attributes
        {
            get { return this.attributes.ToArray(); }
        }

        public CheckIntegrityResult CheckStructureIntegrity(bool usePredefinedConvention)
        {
            //in future version here can be implemented verification logic for column names (they shouldn't violate naming rules of RDBMS etc)
            return new CheckIntegrityResult(true, String.Empty);
        }

        #region Value-based equality semantics implementation
        public override bool Equals(object obj)
        {
            IFieldDescriptor that = obj as IFieldDescriptor;
            if (Object.ReferenceEquals(that, null))
                return false;

            if (String.Compare(this.FieldName, that.FieldName, StringComparison.OrdinalIgnoreCase) == 0 &&
                this.FieldType == that.FieldType)
                return true;
            else
                return false;
        }

        public override int GetHashCode()
        {
            return this.FieldName.GetHashCode();
        }

        public static bool operator ==(FieldDescriptor fd1, FieldDescriptor fd2)
        {
            if (Object.ReferenceEquals(fd1, null))
                return false;
            return fd1.Equals(fd2);
        }

        public static bool operator !=(FieldDescriptor fd1, FieldDescriptor fd2)
        {
            if (Object.ReferenceEquals(fd1, null))
                return false;
            return !fd1.Equals(fd2);
        }
        
        #endregion

        #region IXmlSerializable and auxiliary CreateFromXml member
        public XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(XmlReader reader)
        {
            do
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        {
                            if (string.Compare(reader.Name, FieldDescriptorTag, StringComparison.OrdinalIgnoreCase) == 0)
                            {
                                bool emptyElement = reader.IsEmptyElement;
                                if (reader.HasAttributes)
                                {
                                    while (reader.MoveToNextAttribute())
                                    {
                                        if (string.Compare(reader.Name, FieldNameAttr, StringComparison.OrdinalIgnoreCase) == 0)
                                            this.fieldName = reader.Value;
                                        else if (string.Compare(reader.Name, FieldTypeAttr, StringComparison.OrdinalIgnoreCase) == 0)
                                            this.fieldType = Type.GetType(reader.Value);
                                        else if (string.Compare(reader.Name, FieldAttributesAttr, StringComparison.OrdinalIgnoreCase) == 0)
                                            this.attributes.Add(Int32.Parse(reader.Value));
                                    }
                                }
                                if (emptyElement)
                                    return;
                            }
                        } break;
                    case XmlNodeType.EndElement:
                        {
                            if (string.Compare(reader.Name, FieldDescriptorTag, StringComparison.OrdinalIgnoreCase) == 0)
                                return;
                        } break;
                    default: continue;
                }
            } while (reader.Read());
        }

        public static FieldDescriptor CreateFromXml(XmlReader reader)
        {
            string fieldName = String.Empty;
            Type fieldType = typeof(string);
            List<object> attributes = new List<object>();

            do
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        {
                            if (string.Compare(reader.Name, FieldDescriptorTag, StringComparison.OrdinalIgnoreCase) == 0)
                            {
                                bool emptyElement = reader.IsEmptyElement;
                                if (reader.HasAttributes)
                                {
                                    while (reader.MoveToNextAttribute())
                                    {
                                        if (string.Compare(reader.Name, FieldNameAttr, StringComparison.OrdinalIgnoreCase) == 0)
                                            fieldName = reader.Value;
                                        else if (string.Compare(reader.Name, FieldTypeAttr, StringComparison.OrdinalIgnoreCase) == 0)
                                            fieldType = Type.GetType(reader.Value);
                                        else if (string.Compare(reader.Name, FieldAttributesAttr, StringComparison.OrdinalIgnoreCase) == 0)
                                            attributes.Add(Int32.Parse(reader.Value));
                                    }
                                }
                                if (emptyElement)
                                    return new FieldDescriptor(fieldType, fieldName);
                            }
                        } break;
                    case XmlNodeType.EndElement:
                        {
                            if (string.Compare(reader.Name, FieldDescriptorTag, StringComparison.OrdinalIgnoreCase) == 0)
                                return new FieldDescriptor(fieldType, fieldName);
                        } break;
                    default: continue;
                }
            } while (reader.Read());
            if (attributes.Count == 0)
                return new FieldDescriptor(fieldType, fieldName);
            else
                return new FieldDescriptor(fieldType, fieldName, attributes.ToArray());
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteAttributeString(FieldNameAttr, this.FieldName);
            writer.WriteAttributeString(FieldTypeAttr, this.FieldType.FullName);
            if (this.attributes.Count > 0)
                foreach(object attr in this.attributes)
                    writer.WriteAttributeString(FieldAttributesAttr, attr.ToString());
        }
        #endregion    

    }

}
